package com.ruoyi.eqmonitor.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutionException;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.eqmonitor.dispose.domain.OpentsdbTags;
import com.ruoyi.eqmonitor.domain.OptDeviceMonitorMonth;
import com.ruoyi.eqmonitor.dtos.OptDeviceDto;
import com.ruoyi.eqmonitor.mapper.OptDeviceMonitorMonthMapper;
import com.ruoyi.eqmonitor.service.IOptDeviceMonitorMonthService;
import com.ruoyi.eqmonitor.service.IOptDeviceService;
import com.ruoyi.eqmonitor.utils.Constant;
import com.ruoyi.eqmonitor.utils.OpenTSDBUtils;
import org.apache.http.nio.reactor.IOReactorException;
import org.opentsdb.client.OpenTSDBClient;
import org.opentsdb.client.bean.request.Query;
import org.opentsdb.client.bean.request.SubQuery;
import org.opentsdb.client.bean.response.QueryResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * 设备监测数据——每月数据Service业务层处理
 * 
 * @author ruoyi
 * @date 2023-03-08
 */
@Service
public class OptDeviceMonitorMonthServiceImpl extends ServiceImpl<OptDeviceMonitorMonthMapper, OptDeviceMonitorMonth> implements IOptDeviceMonitorMonthService {
    
    @Autowired
    private OptDeviceMonitorMonthMapper optDeviceMonitorMonthMapper;
    @Resource
    private IOptDeviceService iOptDeviceService;
    @Resource
    private OpenTSDBUtils openTSDBUtils;

//    @Override
//    public List<Map<String, Object>> getBySnCode(List<String> snCodes, String time, List<String> monitorCodes) throws ParseException {
//        Calendar calendar = Calendar.getInstance();
//        String year = calendar.get(Calendar.YEAR)+"";
//
//        //如果查询的是当年，则更新当月的数据
//        if (time.equals(year)){
//            int month = calendar.get(Calendar.MONTH)+1;
//            String dateTime = year+"-"+month;
//            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
//            Date date = sdf.parse(dateTime);
//            calendar.setTime(date);
//            calendar.add(Calendar.DAY_OF_MONTH, 1);
//            Long beginTime = date.getTime();
//            Long endTime = calendar.getTime().getTime();
//            // 过滤所有设备编码
//            String[] deviceCodes = snCodes.toArray(new String[]{});
//            // 创建snCodeFilter过滤器
//            SubQuery.Filter snCodeFilter = OpenTSDBUtils.createSnCodeFilter(deviceCodes);
//            // 创建monitorCode过滤器 过滤监测数据,水质相关
//            SubQuery.Filter monitorCodeFilter = OpenTSDBUtils.createMonitorCodeFilter(
//                    Constant.MonitorInfo.M_0x0592.getCode(),
//                    Constant.MonitorInfo.M_0x05D1.getCode());
//            //查询数据
//            List<QueryResult> queryResults = TSDBQuery(beginTime,endTime, snCodeFilter, monitorCodeFilter);
//            List<Map<String, Object>> maps = DisposeData(queryResults);
//            this.saveData(maps);
//        }
//        //查询
//        List<OptDeviceMonitorMonth> list = optDeviceMonitorMonthMapper.getBySnCode(snCodes, time, monitorCodes);
//        List<Map<String, Object>> vo = new ArrayList<>();
//        //根据设备编码分组
//        Map<String, List<OptDeviceMonitorMonth>> snCodeCollect = list.stream().collect(Collectors.groupingBy(OptDeviceMonitorMonth::getSnCode));
//        for (String snCodeKey : snCodeCollect.keySet()){
//            List<OptDeviceMonitorMonth> snCodeList = snCodeCollect.get(snCodeKey);
//            //根据设备编码获取设备信息
//            OptDeviceDto deviceByCode = iOptDeviceService.getDeviceByCode(snCodeKey);
//            //根据监测编码分组
//            Map<String, List<OptDeviceMonitorMonth>> monitorCodeCollect = snCodeList.stream().collect(Collectors.groupingBy(OptDeviceMonitorMonth::getMonitorCode));
//            for (String monitorCodeKey: monitorCodeCollect.keySet()){
//                List<OptDeviceMonitorMonth> monitorCodeList = monitorCodeCollect.get(monitorCodeKey);
//                List<String> timeList = new ArrayList<>();
//                List<String> avgValueList = new ArrayList<>();
//                List<String> maxValueList = new ArrayList<>();
//                List<String> minValueList = new ArrayList<>();
//                Map<String, Object> tags = new HashMap<>();
//                tags.put("snCode",snCodeKey);
//                tags.put("monitorCode",monitorCodeKey);
//                for (OptDeviceMonitorMonth monitorMonth : monitorCodeList){
//                    timeList.add(monitorMonth.getTime());
//                    avgValueList.add(monitorMonth.getAvgValue());
//                    maxValueList.add(monitorMonth.getMaxValue());
//                    minValueList.add(monitorMonth.getMinValue());
//                    if (tags.get("monitorName") == null){
//                        tags.put("monitorName",monitorMonth.getMonitorName());
//                        tags.put("dataUnit",monitorMonth.getDataUnit());
//                        tags.put("equipmentType",monitorMonth.getEquipmentType());
//                        tags.put("equipmentTypeCode",monitorMonth.getEquipmentTypeCode());
//                    }
//                }
//                Map<String, Object> map = new HashMap<>();
//                map.put("time",timeList);
//                map.put("value",avgValueList);
//                map.put("maxValues",maxValueList);
//                map.put("minValues",minValueList);
//                map.put("device",deviceByCode);
//                map.put("tags",tags);
//                vo.add(map);
//            }
//        }
//        return vo;
//    }

    @Override
    public List<OptDeviceMonitorMonth> getBySnCodeLastMonth(String[] deviceCodes) throws ParseException {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, -1);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        String dateTime = sdf.format(calendar.getTime());
        LambdaQueryWrapper<OptDeviceMonitorMonth> lqw = new LambdaQueryWrapper<>();
        lqw.eq(OptDeviceMonitorMonth::getTime, dateTime)
                .eq(OptDeviceMonitorMonth::getEquipmentTypeCode, Constant.EquipmentType.SE200_L_Y0.getCode())
                .eq(OptDeviceMonitorMonth::getMonitorCode, Constant.MonitorInfo.M_0x05D1.getCode());
        if (deviceCodes.length > 0) {
            List<String> deviceCodeList = new ArrayList<>(Arrays.asList(deviceCodes));
            lqw.in(OptDeviceMonitorMonth::getSnCode, deviceCodeList);
        }
        List<OptDeviceMonitorMonth> list = baseMapper.selectList(lqw);
        if (list == null) {
            // 创建snCodeFilter过滤器
            SubQuery.Filter snCodeFilter = OpenTSDBUtils.createSnCodeFilter(deviceCodes);
            SubQuery.Filter monitorCodeFilter = OpenTSDBUtils.createMonitorCodeFilter(
                    Constant.MonitorInfo.C_CC0007.getCode());
            //获取上个月第一天
            calendar.set(Calendar.DAY_OF_MONTH, 1);
            //获取上个月最后一天
            Calendar calendar1 = Calendar.getInstance();
            calendar1.set(Calendar.DAY_OF_MONTH, 1);
            calendar1.add(Calendar.DATE, -1);

            //查询数据
            List<QueryResult> queryResults = TSDBQuery(calendar.getTime().getTime(), calendar1.getTime().getTime(), snCodeFilter, monitorCodeFilter);
            List<Map<String, Object>> maps = DisposeData(queryResults);
            this.saveData(maps);
            list = baseMapper.selectList(lqw);
        }
        return list;
    }

    @Override
    public boolean selectWaterCondition(String type,String time) throws ParseException {
        // 查询设备
        OptDeviceDto optDeviceDto = new OptDeviceDto();
        optDeviceDto.setDeviceType(type);
        List<OptDeviceDto> llAnDanFlowDevice = iOptDeviceService.selectOptDeviceList(optDeviceDto);
        if (llAnDanFlowDevice.size() == 0) {
            return false;
        }
        String[] deviceCodes = llAnDanFlowDevice.stream().map(OptDeviceDto::getDeviceCode).distinct().toArray(String[]::new);

        SubQuery.Filter snCodeFilter = OpenTSDBUtils.createSnCodeFilter(deviceCodes);
        SubQuery.Filter monitorCodeFilter = OpenTSDBUtils.createMonitorCodeFilter(
                Constant.MonitorInfo.M_0x0592.getCode(),
                Constant.MonitorInfo.M_0x05D1.getCode());

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
        Date timeDate = sdf.parse(time);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(timeDate);
        calendar.add(Calendar.YEAR, 1);
        //查询数据
        List<QueryResult> queryResults = TSDBQuery(timeDate.getTime(),calendar.getTime().getTime(), snCodeFilter, monitorCodeFilter);

        List<OptDeviceMonitorMonth> optDeviceMonitorMonths = new ArrayList<>();
        List<Map<String, Object>> maps = DisposeData(queryResults);
        for (Map<String, Object> map : maps){
            Object tags = map.get("tags");
            String tagsString = tags.toString();
            JSONObject tagsMap = JSON.parseObject(tagsString);
            String snCode = tagsMap.get("snCode").toString();
            String monitorCode = tagsMap.get("monitorCode").toString();
            String monitorName = tagsMap.get("monitorName").toString();
            String dataUnit = tagsMap.get("dataUnit").toString();
            Object equipmentType1 = tagsMap.get("equipmentType");
            if (equipmentType1 == null){
                continue;
            }
            String equipmentType = equipmentType1.toString();
            String equipmentTypeCode = tagsMap.get("equipmentTypeCode").toString();

            Object time1 = map.get("time");
            String timeString = JSONArray.toJSONString(time1);
            List<String> timeList= JSONArray.parseArray(timeString,String.class);
            Object value = map.get("value");
            String valueString = JSONArray.toJSONString(value);
            List<String> valueList= JSONArray.parseArray(valueString,String.class);
            Object maxValues = map.get("maxValues");
            String maxValuesString = JSONArray.toJSONString(maxValues);
            List<String> maxValuesList= JSONArray.parseArray(maxValuesString,String.class);
            Object minValues = map.get("minValues");
            String minValuesString = JSONArray.toJSONString(minValues);
            List<String> minValuesList= JSONArray.parseArray(minValuesString,String.class);
            for (int i=0; i<timeList.size(); i++){
                OptDeviceMonitorMonth monitorMonth = new OptDeviceMonitorMonth();
                monitorMonth.setSnCode(snCode);
                monitorMonth.setMonitorCode(monitorCode);
                monitorMonth.setMonitorName(monitorName);
                monitorMonth.setDataUnit(dataUnit);
                monitorMonth.setEquipmentType(equipmentType);
                monitorMonth.setEquipmentTypeCode(equipmentTypeCode);

                monitorMonth.setTime(timeList.get(i));
                monitorMonth.setAvgValue(valueList.get(i));
                monitorMonth.setMaxValue(maxValuesList.get(i));
                monitorMonth.setMinValue(minValuesList.get(i));
                optDeviceMonitorMonths.add(monitorMonth);
            }

        }
        return this.saveBatch(optDeviceMonitorMonths);
    }

    /**
     * 查询数据
     */
    public List<QueryResult> TSDBQuery(Long beginTime, Long endTime, SubQuery.Filter snCodeFilter, SubQuery.Filter monitorCodeFilter) throws ParseException {
        //创建连接
        OpenTSDBClient queryClient = null;
        try {
            queryClient = openTSDBUtils.readClient();
        } catch (IOReactorException e) {
            log.error("创建opentsdb查询失败");
        }

        // 子查询
        SubQuery sub = SubQuery
                .metric(openTSDBUtils.getMetric())
                // 不聚合
                .aggregator(SubQuery.Aggregator.NONE)
                .filter(snCodeFilter)
                .filter(monitorCodeFilter)
                .build();

        // 查询主体
        Query queryParams =  Query.begin(beginTime).end(endTime)
                .useCalendar().timezone(OpenTSDBUtils.OPENTSDB_TIMEZONE)
                .sub(sub)
                .build();

        List<QueryResult> resultList = new ArrayList<>();
        try {
            // 进行查询
            resultList = queryClient.query(queryParams);
        } catch (IOException | ExecutionException | InterruptedException e) {
            log.error("查询opentsdb失败", e);
        } finally {
            if (queryClient != null) {
                //关闭连接
                try {
                    queryClient.gracefulClose();
                } catch (IOException e) {
                    log.error("关闭opentsdb连接失败");
                }
            }
        }

        return resultList;
    }

    /**
     * 数据处理
     * @param queryResults 需处理的数据
     */
    public List<Map<String, Object>> DisposeData(List<QueryResult> queryResults) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
        List<Map<String, Object>> list = new ArrayList<>();
        for (QueryResult queryResult : queryResults) {
            // 将tags的base64转换为原始的字符串
            OpentsdbTags opentsdbTags = OpenTSDBUtils.decodeToBean(queryResult.getTags());
            Map<String, Object> map = new HashMap<>();
            map.put("tags", opentsdbTags);

            //处理时间和数据
            LinkedHashMap<Long, Number> dps = queryResult.getDps();
            List<String> time = new ArrayList<>();
            List<Number> value = new ArrayList<>();
            List<Double> maxValues = new ArrayList<>();
            List<Double> minValues = new ArrayList<>();
            Double maxValue = 0.000;//标记最大值
            Double minValue = 0.000;//标记最小值
            String timeFlag = null;//根据时间标记重复数据
            Double valueFlag = 0.000;//累计数值
            int i = 0;//累计数值的个数
            for (Map.Entry<Long, Number> entry : dps.entrySet()) {
                Long key = entry.getKey();
                //如果时间戳为空，则赋值0
                if (key == null){
                    key = 0L;
                }
                Date dateTime = new Date(key * 1000L);
                String timeString = sdf.format(dateTime);
                Double v = entry.getValue().doubleValue();
                //第一次，赋值后跳过
                if (timeFlag == null) {
                    timeFlag = timeString;
                    valueFlag = v;
                    maxValue = v;
                    minValue = v;
                    i = 1;
                    continue;
                }
                //时间与上一次相同
                if (timeString.equals(timeFlag)) {
                    valueFlag += v;
                    //获取最大值
                    if (maxValue < v) {
                        maxValue = v;
                    }
                    //获取最小值
                    if (minValue > v) {
                        minValue = v;
                    }
                    i++;
                } else {
                    time.add(timeFlag);
                    BigDecimal bigDecimal = new BigDecimal(valueFlag);
                    value.add(bigDecimal.divide(new BigDecimal(i), 3, BigDecimal.ROUND_HALF_UP));//平均数
                    maxValues.add(maxValue);
                    minValues.add(minValue);

                    //重置
                    timeFlag = timeString;
                    maxValue = v;
                    minValue = v;
                    valueFlag = v;
                    i = 1;
                }
            }
            //最后一条处理完的数据
            time.add(timeFlag);
            BigDecimal bigDecimal = new BigDecimal(valueFlag);
            value.add(bigDecimal.divide(new BigDecimal(i), 3, BigDecimal.ROUND_HALF_UP));
            maxValues.add(maxValue);
            minValues.add(minValue);

            map.put("time", time);
            map.put("value", value);
            map.put("maxValues", maxValues);
            map.put("minValues", minValues);

            list.add(map);
        }
        return list;
    }

    /**
     * 新增
     */
    public void saveData(List<Map<String, Object>> list) {
        for (Map<String, Object> map : list){
            Object tags = map.get("tags");
            String tagsString = tags.toString();
            JSONObject tagsMap = JSON.parseObject(tagsString);
            String snCode = tagsMap.get("snCode").toString();
            String monitorCode = tagsMap.get("monitorCode").toString();
            String monitorName = tagsMap.get("monitorName").toString();
            String dataUnit = tagsMap.get("dataUnit").toString();
            Object equipmentType1 = tagsMap.get("equipmentType");
            if (equipmentType1 == null){
                continue;
            }
            String equipmentType = equipmentType1.toString();
            String equipmentTypeCode = tagsMap.get("equipmentTypeCode").toString();

            Object time1 = map.get("time");
            String timeString = JSONArray.toJSONString(time1);
            List<String> timeList= JSONArray.parseArray(timeString,String.class);
            Object value = map.get("value");
            String valueString = JSONArray.toJSONString(value);
            List<String> valueList= JSONArray.parseArray(valueString,String.class);
            Object maxValues = map.get("maxValues");
            String maxValuesString = JSONArray.toJSONString(maxValues);
            List<String> maxValuesList= JSONArray.parseArray(maxValuesString,String.class);
            Object minValues = map.get("minValues");
            String minValuesString = JSONArray.toJSONString(minValues);
            List<String> minValuesList= JSONArray.parseArray(minValuesString,String.class);
            for (int i=0; i<timeList.size(); i++){
                LambdaQueryWrapper<OptDeviceMonitorMonth> lqw = new LambdaQueryWrapper<>();
                lqw.eq(OptDeviceMonitorMonth::getSnCode,snCode)
                        .eq(OptDeviceMonitorMonth::getMonitorCode,monitorCode)
                        .eq(OptDeviceMonitorMonth::getTime,timeList.get(i));
                OptDeviceMonitorMonth one = this.getOne(lqw);
                if (one != null){
                    one.setAvgValue(valueList.get(i));
                    one.setMaxValue(maxValuesList.get(i));
                    one.setMinValue(minValuesList.get(i));
                    this.updateById(one);
                }else {
                    OptDeviceMonitorMonth monitorMonth = new OptDeviceMonitorMonth();
                    monitorMonth.setSnCode(snCode);
                    monitorMonth.setMonitorCode(monitorCode);
                    monitorMonth.setMonitorName(monitorName);
                    monitorMonth.setDataUnit(dataUnit);
                    monitorMonth.setEquipmentType(equipmentType);
                    monitorMonth.setEquipmentTypeCode(equipmentTypeCode);
                    monitorMonth.setTime(timeList.get(i));
                    monitorMonth.setAvgValue(valueList.get(i));
                    monitorMonth.setMaxValue(maxValuesList.get(i));
                    monitorMonth.setMinValue(minValuesList.get(i));
                    this.save(monitorMonth);
                }
            }
        }
    }

}
